package org.tulkas.selector;

import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public class Selector {
	public static final int COMBINATOR_DESCENDANT = ' ';
	public static final int COMBINATOR_CHILD = '>';
	public static final int COMBINATOR_ADJACENT_SIBLING = '+';
	public static final int COMBINATOR_GENERAL_SIBLING = '~';

	private String text;

	private Filter filter;

	protected Selector(String text) {
		this.text = text;
		FilterBuilder builder = new FilterBuilder();
		Tokenizer tokenizer = new Tokenizer(builder);
		tokenizer.tokenize(text);
		this.filter = builder.getResult();
	}

	public static Selector compile(String text) {
		return new Selector(text);
	}

	public Element[] select(Element root) {
		List<Element> results = new ArrayList<Element>();
		process(root, results, false);
		return results.toArray(new Element[results.size()]);
	}

	public Element selectFirst(Element root) {
		List<Element> results = new ArrayList<Element>();
		process(root, results, true);
		if (results.size() > 0) {
			return results.get(0);
		} else {
			return null;
		}
	}

	protected void process(Element element, List<Element> results,
			boolean returnFirst) {
		Node node = element;

		while (node != null) {
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (filter.matches(ele)) {
					results.add(ele);
				}

				if (returnFirst) {
					break;
				}
			}

			if (node.hasChildNodes()) {
				node.getFirstChild();
			} else {
				while (node.getNextSibling() == null) {
					node = node.getParentNode();
				}
				if (node == element) {
					break;
				}
				node = node.getNextSibling();
			}
		}
	}

	@Override
	public String toString() {
		return new StringBuffer().append("[").append(text).append("] - ")
				.append(filter.toString()).toString();
	}

}
